#!/bin/bash

me="$(readlink -f "$0")"
cd "${me%/*}" # this is the working directory throughout the whole script
# for sourcing plugins & help text
# if cd fails, the script will still work

# load sleep as a builtin if available - portable enough like this?
for file in /usr/lib/bash/sleep /usr/lib64/bash/sleep /usr/lib32/bash/sleep; do
    [ -r "$file" ] && enable -f "$file" sleep && break
done
unset file

function help {
. help.less
[[ "$1" == noless ]] && echo "$text" && exit 0
echo "$text" | less -FMRiLnQX -PM"?eEnd - press q to exit - more information\\: github\\.com/ohnonot/media-player-info:More" 
exit 0
}

function usage {
    [[ "$*" != "" ]] && echo "
    $*"
    echo "
    Try \"$me -h\" for a full description
"
    exit 1
}

function upvote_func {
	[[ "$(head -1 "$history_file")" != *"$upvote_str" ]] && upvote_str="$sep$upvote_str"
	sed -i "1 s/$/$upvote_str/" "$history_file"
	notify-send -u low "$me" "Upvoted:\n$(head -1 "$history_file")"
}

function update_history {
printf "$(date '+%F %R')$sep${info[${chosen}2]}$sep${info[${chosen}1]}\n" > "$history_file".temp
cat "$history_file"  >> "$history_file".temp
mv "$history_file".temp "$history_file"
rm "$history_file".temp
}

function only_me_or_exit {
# argument: pidfile
# make sure only 1 instance is running
touch "$1"
read lastPID < "$1"
# if lastPID is not null and a process with that pid exists , exit
[ ! -z "$lastPID" -a -d /proc/$lastPID ] && { echo "An instance of $me is already running with pid $lastPID." ; exit 1 ; }
# else - save my pid in the lock file, and continue
echo $$ > "$1"
}

function exit_only_me {
# argument1: pidfile
# argument2: message (opt.)
[[ "$2" != "" ]] && echo "$2"
rm "$1"
exit 0
}

##############################################################################
# player plugins work like this:
# - $player is the name of the player executable so that "pgrep -x $player" will tell us whether
#   that player is running.
# - any file inside the plugins directory called exactly ${player}.mpip will be sourced
#   automatically (unless the user specified) by the main script
# - it contains at least one function named $player_fillarray
# - the global associative array info[] is defined in the main script.
# - the function fills it with fields named ${player}0 - ${player}8 - example: info[audacious0]=...
#
#   like this:
# - 0  the player's state play/pause/stop/unknown as defined in the state_nice array in the main script
# - 1  the title of the currently playing song, usually 'Song Name - Artist' or, failing that, file name
# - 2  the file (usually full path)
# - 3  the time position in the song as elapsed/total, or merely elapsed for streams
# - 4  player's play/pause toggle command
# - 5  player's next song command
# - 6  player's previous song command
# - 7  player's show window command
# - 8  icon name (as defined in global "icons" array) for the current state
#
# this array will later be filled with 2 more fields, but this is not player specific,
# hence it is not part of the function.
# see help text for -d option.
#
# if the function is called with "dryrun" as $1, it will not query the player
# but fill the array with all static values available, i.e. fields 4-7.
#
# these helper functions can be utilized:
#    title_concat (formats the title to artist - songtitle, except when we listen to a
#                  network stream, because those usually already format the songtitle
#                  as artist - songtitle)
#    filename_as_title (if no title, use filename instead)
#    time_concat (concatenates the fields for time elapsed/remaining and total time
#
# the internal player functions and helper functions use a trick: the array is first
# filled with: artist,songtitle,file,currenttime,totaltime,playerstate
# all in separate fields, then the title_concat and time_concat functions reformat what
# is to become the final title and time field, after which the artist (field 0) is
# overwritten with the player's beautified state. now the last two array fields are
# useless and are overwritten with other information.
##############################################################################

############# PLAYER: MOC ###################################################
function mocp_fillarray {
if [[ "$1" != "dryrun" ]]; then
    i=0
    while read info[${player}$i]; do
        ((i++))
    done < <(mocp -Q '%artist\n%song\n%file\n%ct\n%tt\n%state' 2>/dev/null)

    title_concat
    # replacing mocp's status with a nicer one
    case "${info[${player}5]}" in
    PLAY)
        info[${player}0]="${state_nice[0]}" # Playing
        [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
    ;;
    PAUSE)
        info[${player}0]="${state_nice[1]}" # Paused
        [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
    ;;
    STOP)
        info[${player}0]="${state_nice[2]}" # Stopped
        [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
        [[ "{info[${player}1]}" == "" ]] && info[${player}1]="$player: ${state_nice[2]}"
    ;;
    *)
        info[${player}0]="${state_nice[3]}" # State_Unknown
        [[ "$icondir" != "" ]] && info[${player}8]="${icons[3]}" # icons use different names
    ;;
    esac
    filename_as_title
    time_concat
fi
info[${player}4]="mocp -G"
info[${player}5]="mocp -f"
info[${player}6]="mocp -r"
info[${player}7]="$terminal -e mocp"
}
############# PLAYER: AUDACIOUS ##############################################
function audacious_fillarray {
if [[ "$1" != "dryrun" ]]; then
    # get the info
    i=0
    while read info[${player}$i]; do
        ((i++))
    done < <(audtool current-song-tuple-data artist current-song-tuple-data title current-song-filename current-song-output-length current-song-length playback-status)
    for (( ; i < 6 ; i++ )); do
        info[${player}$i]=''
    done
    title_concat
    case "${info[${player}5]}" in
        playing)
            info[${player}0]="${state_nice[0]}" # Playing
            [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
        ;;
        paused)
            info[${player}0]="${state_nice[1]}" # Paused
            [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
        ;;
        stopped)
            info[${player}0]="${state_nice[2]}" # Stopped
            [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
            [[ "${info[${player}1]}" == "" ]] && info[${player}1]="$player: ${state_nice[2]}"
        ;;
        *)
            info[${player}0]="${state_nice[3]}" # State_Unknown
        ;;
    esac
    filename_as_title
    time_concat
fi
info[${player}4]="audacious -t"
info[${player}5]="audacious -f"
info[${player}6]="audacious -r"
info[${player}7]="audacious -m"
}
############# PLAYER: MPD ##############################################
function mpd_fillarray {
player_ui="$(which ncmpcpp 2>/dev/null)"
[[ "$player_ui" == "" ]] && player_ui="$(which ncmpc 2>/dev/null)"
if [[ "$1" != "dryrun" ]]; then
    # get the info
    i=0
    while read info[${player}$i]; do
        ((i++))
    done < <(mpc -f '%artist%\n%title%\n%file%\n%name%')

    # if player is stopped, there's no metadata, and mpc won't display anything either
    # - it goes straight to the status line, no matter how the metadata request is
    # formatted. in that case, we fill the data manually.
    if [[ "${info[${player}0]}" == "volume:"* ]]; then
        info[${player}0]="${state_nice[2]}" # Stopped
        info[${player}1]="$player: ${state_nice[2]}"
        info[${player}2]=""
        info[${player}3]=""
        [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
    else
        title_concat 
        auxarr=( ${info[${player}4]} ) # mpc puts some needed information on a single line that needs to be parsed separately
        case "${auxarr[0]}" in
            '[playing]')
                info[${player}0]="${state_nice[0]}" # Playing
                [[ "$icondir" != "" ]] && info[${player}8]="${icons[0]}" # icons use different names
            ;;
            '[paused]')
                info[${player}0]="${state_nice[1]}" # Paused
                [[ "$icondir" != "" ]] && info[${player}8]="${icons[1]}" # icons use different names
            ;;
            *)
                info[${player}0]="${state_nice[2]}" # Stopped (special case with mpd - whenever this function is called , it means the daemon is running. when it's neither playing nor paused, one can assume a stopped state - State_Unknown is not needed)
                [[ "$icondir" != "" ]] && info[${player}8]="${icons[2]}" # icons use different names
            ;;
        esac
        if [[ "${info[${player}1]}" == "" ]] && [[ "${info[${player}3]}" != "" ]];then
            info[${player}1]="${info[${player}3]}"
        else
            filename_as_title
        fi
        [[ "$music_directory" != "" ]] && [[ "${info[${player}2]}" != http* ]] && info[${player}2]="$music_directory/${info[${player}2]}"
        info[${player}3]="${auxarr[2]%\/0:00}"
    fi
fi
info[${player}4]="mpc toggle"
info[${player}5]="mpc next"
info[${player}6]="mpc prev"
[[ "$player_ui" != "" ]] && info[${player}7]="$terminal -e $player_ui" || info[${player}7]="$terminal -title No_player_ui_found -e bash -c read"
}
#############################################################################
# LOGIC COMMON TO ALL PLAYERS
###########################################################################
function filename_as_title {
# if info[${player}1] - that's the title - is empty, use the filename instead
if [[ "${info[${player}1]}" == "" ]]
then
    info[${player}1]="${info[${player}2]##*/}" # basename, but...
    # ...if it's shorter than 10 chars, use the full path/filename
    (( ${#info[${player}1]} < 10 )) && info[${player}1]="${info[${player}2]}"
    # remove http(s):// from front of string
    info[${player}1]="${info[${player}1]#http\:\/\/}"
    info[${player}1]="${info[${player}1]#https\:\/\/}"
fi
}
function time_concat {
# if total time is zero, display only current time
# at the same time, leading double zeros are replaced by one zero
if [[ "${info[${player}4]}" == "" ]] || [[ "${info[${player}4]}" =~ 0*":00" ]]; then
    info[${player}3]="${info[${player}3]/#00/0}"
else
    info[${player}3]="${info[${player}3]/#00/0}/${info[${player}4]/#00/0}"
fi
}
function title_concat { 
# if http != network stream, concatenate %artist% and %title%
# if http == network stream, %title% is very likely to already contain artist - title info:
[[ "${info[${player}0]}" != "" ]] && [[ "${info[${player}1]}" != "" ]] && [[ "${info[${player}2]}" != http* ]] && info[${player}1]="${info[${player}0]}$sep${info[${player}1]}"
}
function get_art {
# if the directory of the currently playing song exists (i.e. it is a local file)
# try to find some albumart in it.
if [ -d "${info[${player}2]%/*}" ]; then
    for string in $arts; do
        for ext in $extensions; do
            # if there's an image with the current song's exact name, that wins:
            [ -r "${info[${player}2]%.*}".$ext ] && \
            info[${player}8]="${info[${player}2]%.*}.$ext" && return
            for file in "${info[${player}2]%/*}"/*.$ext; do
                [[ "${file,,}" == */${string}*.$ext ]] && \
                info[${player}8]="$file" && return
            done
        done
        # still no artwork? take ANY file with a valid extension (i.e. any image)
        for ext in $extensions; do
            for file in "${info[${player}2]%/*}"/*.$ext; do
                [ -r "$file" ] && info[${player}8]="$file" && return
            done
        done
        # still no artwork? try to extract an image with ffmpeg - if directory is writable
        [ -w "${info[${player}2]%/*}" ] && ffmpeg -i "${info[${player}2]}" "${info[${player}2]%/*}"/folder.jpg >/dev/null 2>&1 && info[${player}8]=folder.jpg
    done
fi
}
function join_maxlen_func {
# 2 strings - $1 and $2 - not more than $maxlen chars altogether.
# but the first string is to be shortened, not the 2nd.
if [[ "$2" != "" ]] ; then
    [ ${#1} -lt $((maxlen - ${#2} - ${#dots})) ] && dots=""
    echo "${1:0:((maxlen - ${#dots} - ${#2}))}$dots $2"
else
    [ ${#1} -le $(( maxlen )) ] && dots=""
    echo "${1:0:((maxlen - ${#dots}))}$dots"
fi
}
function lines_func {
# cuts the input string into approx. equally long lines up to $lines.
# this is not exact, however the exact number of lines _must_ be returned
# otherwise tint2's exec function cannot work in continuous mode
string="$1"
returnarray=() # each array pos. will contain 1 line
local linecount=$lines
for (( arraypos=0 ; linecount > 1 ; linecount-- ))
do
    length=${#string}
    for (( fwd=$((length/linecount)) , bwd=$((length/linecount)) ; bwd >= 0 ; fwd++ , bwd-- ))
    do
        if [[ "${string:$fwd:1}" = " " ]]
        then
            returnarray[arraypos]="${string:0:$fwd}"
            arraypos=$((arraypos + 1))
            string="${string:$((fwd + 1))}"
            break
        fi
        if [[ "${string:$bwd:1}" = " " ]]
        then
            returnarray[arraypos]="${string:0:$bwd}"
            arraypos=$((arraypos + 1))
            string="${string:$((bwd + 1))}"
            break
        fi
    done
done
returnarray[arraypos]="$string"
arraypos=$((arraypos + 1))

if [[ "$tint2" == "1" ]]; then max=$lines ; else max=$arraypos ; fi

for (( c=0 ; c < max ; c++ )); do
    echo "${returnarray[c]}"
done
}

#############################################################################
# ┏┳┓┏━┓╻┏┓╻
# ┃┃┃┣━┫┃┃┗┫
# ╹ ╹╹ ╹╹╹ ╹
#############################################################################

# Our main info array
declare -A info=()

which "${0##*/}" >/dev/null 2>&1 && me="${0##*/}" || me="$0"
[ -w "/dev/shm" ] && tmp_dir="/dev/shm" || tmp_dir="/tmp"
lines=1
maxlen=60 # maximum length of string before dividing into lines
showtime=0 # display current/total time or not
continuous=0 
onlyone=0
altcmd=''
stopped=0
paused=0
dump=''
alwaysfirst=0
tint2=0
# icons
icondir='' # will look for 3 icons in dir: play.* pause.* stop.* unknown.*
extensions="png svg gif PNG jpg jpeg GIF JPG JPEG" # these extensions seem to be supported by tint2
icons=( Playing Paused Stopped StateUnknown Altcmd ) # ordering has to be the same as in state_nice array, and don't change Altcmd - it's hardcoded in MAIN
state_nice=( Playing Paused Stopped StateUnknown )
# nice strings to display the four player states. these are also the basenames for the icons, e.g.: Playing.png or "StateUnknown.gif" etc.
dots="…"
sep=" - "
default_icon="audio-speakers-symbolic"
showart=0
icon_art=0
arts="folder cover album front art"
selected_players=""
supported_players="mpd audacious mocp" # builtins
plugindir="plugins"
music_directory=""
notify=0
history=0
history_file=""
watch_song_change=0
delay=0
upvote_str="☻"

# assume that the $TERMINAL environment variable contains the path to a usable executable
# if not, use XTERMINAL, and if that's empty, use 'xterm'
[[ "$TERMINAL" != "" ]] && terminal="$TERMINAL" || terminal=${XTERMINAL-xterm}

while getopts "l:w:Tc:t:p:PSd:Ds:i:a:e:r:nAIM:1hH:y:Uu:X" opt; do
  case $opt in
    l) # lines
      lines="$OPTARG"
      [ "$lines" -lt 1 ] || [ "$lines" -gt 100 ] && usage "-$opt: Invalid number: $lines"
      ;;
    w) # width = maxlen per line
      maxlen="$OPTARG"
      [ "$maxlen" -lt 10 ] && usage "-$opt: Invalid number: $maxlen"
      ;;
    T) # show time elapsed/remaining?
      showtime=1
      ;;
    c) # run script continuously? - interval in s
      continuous="$OPTARG"
      [ "$continuous" -lt 1 ] && usage "-$opt: Invalid number: $continuous"
      ;;
    t) # run script continuously in tint2 mode - interval in s
      continuous="$OPTARG"
      tint2=1
      [ "$continuous" -lt 1 ] && usage "-$opt: Invalid number: $continuous"
      ;;
    p) # which media players?
      selected_players="$OPTARG"
      ;;
    P) # include "Paused" to the list of valid responses?
      paused=1
      ;;
    S) # include "Stopped" to the list of valid responses?
      stopped=1
      paused=1
      ;;
    d) 
      [[ "${OPTARG:0:1}" != '/' ]] && usage "-$opt: use absolute path please. \"$OPTARG\" is invalid."
      [ -w "${OPTARG%/*}" ] && dump="$OPTARG" || usage "-$opt: Cannot create file \"$OPTARG\""
      ;;
    D) # show window command always for first player
      alwaysfirst=1
      ;;
    s) # nice strings for player states
      state_nice=( $OPTARG )
      (( "${#state_nice[@]}" != 4 )) && usage "-$opt: \"${state_nice[*]}\" is not sufficient. Please provide exactly 4 strings."
      ;;
    i) # icon or not? which directory?
      icondir="$OPTARG"
      [ ! -d "$icondir" ] && usage "-$opt: The directory $icondir does not exist."
      ;;
    a) # alternative command when no media player is running
        altcmd="$OPTARG"
        #~ [ ! -x ${altcmd%% *} ] && ! which ${altcmd%% *} >/dev/null 2>&1 && usage "-$opt: Command \"$altcmd\" is not executable or not in PATH"
      ;;
    e) dots="$OPTARG"
      ;;
    r) sep="$OPTARG"
      ;;
    n)  notify=1 && watch_song_change=1
      ;;
    y)  delay="$OPTARG"
      [ "$delay" -lt 1 ] && usage "-$opt: Invalid number: $delay"
      ;;
    A)  showart=1
      ;;
    I)  icon_art=1
        showart=1
      ;;
    M) # define mpd's music directory
      [ ! -d "$OPTARG" ] && usage "-$opt: The directory $OPTARG does not exist."
      music_directory="${OPTARG%/}"
      ;;
    1) # make sure only one instance is running
        onlyone=1
      ;;
    h) help
      ;;
    H) touch "$OPTARG" || usage "-$opt: Unable to touch file $OPTARG"
       history=1 && history_file="$OPTARG" && watch_song_change=1
      ;;
    U) upvote=1
      ;;
    u) upvote_str="$OPTARG"
      ;;
    X) help noless
      ;;
    *) usage
      ;;
  esac
done

[[ "$upvote" == 1 ]] && upvote_func

# Can't watch for changed songs when there's no dump file to check
[[ "$dump" == "" ]] && watch_song_change=0

# make sure only 1 instance is running
if [[ "$onlyone" = 1 ]]; then
    [ -w "$tmp_dir" ] || usage "-1: $tmp_dir is not writable"
    pidfile="$tmp_dir/${me}_pid"
    only_me_or_exit "$pidfile"
fi

if [[ "$selected_players" == "" ]]; then
# user made no choice, compile a list of player functions, source plugins
    selected_players="$supported_players" # builtins
    shopt -s nullglob
    for plugin in "$plugindir"/*.mpip; do
        . "$plugin" # source it!
        plugin="${plugin#$plugindir/}"
        plugin="${plugin%.mpip}" # it is now stripped to its basename minus extension
        [[ "$selected_players" != *$plugin* ]] && selected_players="$selected_players $plugin"
    done
    shopt -u nullglob
else
# user made a list, use those, load external plugins if found
    for player in $selected_players ; do
        player="$plugindir/${player}.mpip"
        [ -r "$player" ] && . "$player"
    done
    # if a now loaded plugin has the same name as the builtin, it will override it.
fi

# don't search for icons we are not going to use
if [[ "$icondir" != "" ]]; then
    [[ "$stopped" == "0" ]] && delete=(Stopped) && icons=( ${icons[@]/$delete} )
    [[ "$paused" == "0" ]] && delete=(Paused) && icons=( ${icons[@]/$delete} )
fi

while true ; do
    iconstring=''
    chosen=""
    playerson=''
    [[ "$tint2" == "1" ]] && >&2 printf '\e[2J'
    for player in $selected_players ; do
    # collecting all the info from all players
        pgrep -x $player >/dev/null 2>&1 || continue
        playerson="$playerson $player"
        ######################################################################
        ${player}_fillarray # this should be the only player-specific function
        info[${player}9]="$player" #
        ######################################################################
        # different output (e.g. tooltip) to stderr:
        >&2 printf "%s" "${info[${player}0]}: $player"
        [[ "${info[${player}1]}" != "" ]] && >&2 printf "\n%s" "${info[${player}1]}"
        [[ "${info[${player}3]}" != "" ]] && >&2 printf "\n%s" "${info[${player}3]}"
        [[ "${info[${player}2]}" != "" ]] && >&2 printf "\n%s" "${info[${player}2]}"
        >&2 printf "\n"
    done

    # find the $chosen player, whose info we will display
    for player in $playerson ; do
        if [[ "${info[${player}0]}" == "${state_nice[0]}" ]]; then
            chosen="$player" # if it plays, it wins.
            # optionally look for artwork for local files
            [[ "$showart" == "1" ]] && info[${player}8]="" && [[ "${info[${player}2]}" != http* ]] && get_art
            # optionally send notification
            if [[ "$watch_song_change" == "1" ]] && [[ "$dump" != "" ]] && [[ "$(sed -n 2p "$dump")" != "${info[${chosen}1]}" ]]; then
                if [[ "$notify" == 1 ]]; then
                    [ -r "${info[${chosen}8]}" ] && icon="${info[${chosen}8]}" || icon="$default_icon"
                    (sleep "$delay" && notify-send -i "$icon" "${info[${chosen}1]}" "$chosen ${info[${chosen}0],,}") &
                fi
            fi
			if [[ "$history" == 1 ]] && [ -r "$history_file" ] && [ -w "$history_file" ] && [[ "$(head -1 "$history_file")" != *"${info[${chosen}1]}"* ]]; then
				update_history
			fi
            break
        fi
    done
    if [[ "$chosen" == "" ]]; then
    # otherwise make a choice when -P option is active...
        if [[ "$paused" == "1" ]]; then
            for player in $playerson ; do
                [[ "${info[${player}0]}" == "${state_nice[1]}" ]] && chosen="$player" && break
            done
        fi
        # ... and with -S option:
        if [[ "$chosen" == "" ]] && [[ "$stopped" == "1" ]]; then
            for player in $playerson ; do
                [[ "${info[${player}0]}" == "${state_nice[2]}" ]] && chosen="$player" && break
            done
        fi
    fi

    # feed the ellipsizing function with data and define icon, depending on choice:
    if [[ "$chosen" == "" ]] && [[ "$altcmd" != "" ]]; then 
        if [[ "$icondir" != "" ]]; then
            for ext in $extensions
            do
            [[ -f "$icondir/Altcmd.$ext" ]] && iconstring="$icondir/Altcmd.$ext" && break
            done
            [[ "$iconstring" == '' ]] && iconstring="no icon"
        fi
        infostring="$(join_maxlen_func "$($altcmd)")"
    else
        [[ "$showtime" == "1" ]] && [[ "${info[${chosen}3]}" != "" ]] &&\
        infostring="$(join_maxlen_func "${info[${chosen}1]}" "${info[${chosen}3]}")" \
        || infostring="$(join_maxlen_func "${info[${chosen}1]}")"
        # make song art the panel icon
        [[ "$icon_art" == "1" ]] && [[ "${info[${chosen}8]}" != "" ]] && \
            [ -r "${info[${chosen}8]}" ] && iconstring="${info[${chosen}8]}"
        # if we don't have an icon yet, and an icondir is defined, get one from there
        if [[ "$icondir" != "" ]] && [[ "$iconstring" == "" ]]; then
            for ext in $extensions
            do
            [ -r "$icondir/${info[${player}8]}.$ext" ] && iconstring="$icondir/${info[${player}8]}.$ext" && break
            done
            [[ "$iconstring" == '' ]] && iconstring="no icon"
        fi
    fi

    # dump info to file. if no player was chosen, choose first player from list of players
    if [[ "$dump" != "" ]] ; then
        [[ "$chosen" == "" ]] && chosen="${selected_players%% *}"
        # if no player is running we still need to fill the array with data!
        [[ "$playerson" == "" ]] && player="$chosen" && ${player}_fillarray dryrun && info[${player}9]="$player"
        # -D option:
        [[ "$alwaysfirst" == "1" ]] && info[${chosen}7]="${info[${selected_players%% *}7]}"
        for (( i=0 ; i < 10 ; i++ )); do
        echo "${info[${chosen}$i]}"
        done > "$dump"
    fi

    if [[ "$infostring" == "" ]] && [[ "$tint2" == "1" ]]; then
        [[ "$icondir" != "" ]] && max=$((lines +1)) || max=$lines
        for (( c=0 ; c < max ; c++ )); do echo; done
    else
        [[ "$iconstring" != '' ]] && echo "$iconstring"
        [[ "$infostring" != '' ]] && lines_func "$infostring"
    fi

    if (( continuous <= 0 )); then
        [[ "$onlyone" == 1 ]] && exit_only_me "$pidfile"
        exit 0
    fi
    sleep "$continuous"
done

exit 0
